/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\scanmethodping.h"

//--------------------------------------------------------------------

ScanMethodPing::ScanMethodPing(void) : ScanMethod()
{
	myType = __wmtPing;
}

ScanMethodPing::~ScanMethodPing(void)
{
}

//--------------------------------------------------------------------

int ScanMethodPing::InternalInit(void)
{
	FillRandomBuffer();

	if(workPhase==__wptDiscovery) return MSR_ERR_OK;
	if(workPhase==__wptScan)      return MSR_ERR_OK;

	return MSR_ERR_PHASE;
}

//--------------------------------------------------------------------

int ScanMethodPing::InternalExecute(void)
{
	ScanPacket* pack;

	if(addrList==NULL) return MSR_ERR_PARAMETER;

	ScanPair sp;
	while(GetNextPair(&sp, __sptIpOnly) && !stopStopStop){
		if(PacketsUsed()>0){
			CheckResponses();
			CheckDeaths();
		}

		if(sp.addr!=NULL){
			if(!sp.addr->addrIP.IsEmpty()){
				if(workPhase==__wptDiscovery && sp.addr->alive){
					// it's the discovery phase and a previous discovery method has found it already
					// so skip this one
				}
				else{
					if(config->xMinDelay>0){
						if(lastScanTime!=0){
							waitDone = false;
							while(!waitDone){
								CheckResponses();
								SleepEx(1, TRUE);
								scanTime = GetTickCount();
								scanTime = ComputeDeltaTime(lastScanTime, scanTime);
								if(scanTime>=config->xMinDelay)
									waitDone = true;
							}
						}
						lastScanTime = GetTickCount();
					}

					pack = FindFirstUnusedPacket();
					while(pack==NULL){
						SleepEx(waitTime, TRUE);
						CheckResponses();
						CheckDeaths();
						pack = FindFirstUnusedPacket();
					}

					if(pack!=NULL){
						pack->Initialize(true);
						pack->sTime       = GetTickCount();
						pack->addr        = sp.addr->addrIP.GetD();
						pack->port        = 0;
						pack->methodType  = myType;
						pack->fullAddress = sp.addr;
						pack->machineInfo = FindMachineByIp(pack->addr);

						//DelayBetweenScans();

						if(!SendIcmpEcho(sp.addr))
							pack->Initialize(false);

						if((packSent % notifTcp) == 0 && packSent>0)
							if(hWnd!=NULL)
								PostMessage(hWnd, CHS_MSG_PACKETS_SENT, 0, (LPARAM)packSent);
					}
				}
			}
		}
	}

	// wait until the end
	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------
void ScanMethodPing::ComputeRandomValues(void)
{
	// nothing to do
}

bool ScanMethodPing::SendIcmpEcho(MFullAddress* addrIn)
{
	BYTE srcMac[6];
	BYTE* payload;
	int payloadLength;
	bool res;

	if(addrIn==NULL) return false; // nothing to send

	ComputeRandomValues();

	if(scanParameters.GetLength()==0){
		res = packetBuilderEx->BuildPackIcmpEchoRequest(winPcap->GetLocalIP(), addrIn->addrIP.GetD());
	}
	else{
		payload = (BYTE*)(const char*)scanParameters;
		payloadLength = scanParameters.GetLength();
		if(payloadLength>1024)
			payloadLength = 1024;
		res = packetBuilderEx->BuildPackIcmpEchoRequestWithUserData(winPcap->GetLocalIP(), addrIn->addrIP.GetD(), payload, payloadLength);
	}
	if(!res) return false;

	winPcap->FillSrcMAC(&srcMac[0]);


//	res = packetBuilderEx->AddEthernetHeaderDotQ(srcMac, addrIn->addrMAC.addr, 0x0800, 4, 5);


	res = packetBuilderEx->AddEthernetHeader(srcMac, addrIn->addrMAC.addr, 0x0800);
	if(!res) return false;

	return SendPacket(packetBuilderEx->GetPacket(), (int)packetBuilderEx->GetPacketSize());

/*
int vlanId;

	for(vlanId=0; vlanId<10; vlanId++){
		packetBuilderEx->BuildPackIcmpEchoRequest(winPcap->GetLocalIP(), addrIn->addrIP.GetD());
		winPcap->FillSrcMAC(&srcMac[0]);
		packetBuilderEx->AddEthernetHeaderDotQDotQ(srcMac, addrIn->addrMAC.addr, 0x0800, 4, 5, 4, vlanId);
		SendPacket(packetBuilderEx->GetPacket(), (int)packetBuilderEx->GetPacketSize());
	}
*/
}

//--------------------------------------------------------------------

void ScanMethodPing::ProcessEchoReply(BYTE* dataIn, int sizeIn)
{
	in_addr inAddr;
	ScanPacket* pack;
	IPHeaderPG* iph;
	MFullAddress* addr;
	MMachineInfo* machineInfo;

	if(sizeIn<20) return;

	iph = (IPHeaderPG*)dataIn;

	inAddr.S_un.S_un_b.s_b1 = iph->Source[0];
	inAddr.S_un.S_un_b.s_b2 = iph->Source[1];
	inAddr.S_un.S_un_b.s_b3 = iph->Source[2];
	inAddr.S_un.S_un_b.s_b4 = iph->Source[3];

	pack = FindPacket(inAddr.S_un.S_addr, 0, myType);
	if(pack!=NULL){
		if(workPhase==__wptDiscovery){
			addr = pack->fullAddress;
			if(addr!=NULL)
				addr->alive = true;
		}
		else{
			machineInfo = pack->machineInfo;
			if(machineInfo!=NULL)
				machineInfo->pingReplayTime = ComputeDeltaTime(pack->sTime, GetTickCount());
		}

		pack->Initialize(false);

//		CString str;
//		str.Format(_T("--- Received ICMP Reply from %d.%d.%d.%d\n"), iph->Source[0], iph->Source[1], iph->Source[2], iph->Source[3]);
//		OutputDebugString(str);
	}
}

//--------------------------------------------------------------------

void ScanMethodPing::ProcessHostUnreacheable(BYTE* dataIn, int sizeIn)
{
	in_addr inAddr;
	ScanPacket* pack;
	IPHeaderPG* iph;

	if(sizeIn<28) return;

	iph = (IPHeaderPG*)dataIn;

	inAddr.S_un.S_un_b.s_b1 = iph->Destination[0];
	inAddr.S_un.S_un_b.s_b2 = iph->Destination[1];
	inAddr.S_un.S_un_b.s_b3 = iph->Destination[2];
	inAddr.S_un.S_un_b.s_b4 = iph->Destination[3];

	pack = FindPacket(inAddr.S_un.S_addr, 0, myType);
	if(pack!=NULL){
		pack->Initialize(false);

//		CString str;
//		str.Format(_T("--- Received Host Unreacheable for %d.%d.%d.%d\n"), iph->Destination[0], iph->Destination[1], iph->Destination[2], iph->Destination[3]);
//		OutputDebugString(str);
	}
}

//--------------------------------------------------------------------
